/*******************************************************************************
* @file Mem.c
* @author hai
* @version V1.0
* @date 2016-1-16
* @brief
********************************************************************************/

#include "Sys.h"

#define MIN_SIZE                          12
#define SIZEOF_STRUCT_MEM       (sizeof(struct mem) + \
                                                    (((sizeof(struct mem) % MEM_ALIGNMENT) == 0)? 0 : \
                                                    (4 - (sizeof(struct mem) % MEM_ALIGNMENT))))
 
struct mem {
    mem_size_t next, prev;
    INT32U used;
};


static struct mem *ram_end;
static INT8U ram[MEMALLOC_SIZE + sizeof(struct mem) + MEM_ALIGNMENT];
                                                    
static struct mem *lfree; /* pointer to the lowest free block */

static void plug_holes(struct mem *mem)
{
    struct mem *nmem;
    struct mem *pmem;
    
    nmem = (struct mem *)&ram[mem->next];
    if (mem != nmem && nmem->used == 0 && (INT8U *)nmem != (INT8U *)ram_end) {
        if (lfree == nmem) {
            lfree = mem;
        }
        
        mem->next = nmem->next;
        ((struct mem *)&ram[nmem->next])->prev = (INT8U *)mem - ram;
    }
    // plug hole backward
    pmem = (struct mem *)&ram[mem->prev];
    if (pmem != mem && pmem->used == 0) {
        if (lfree == mem) {
            lfree = pmem;
        }
        pmem->next = mem->next;
        ((struct mem *)&ram[mem->next])->prev = (INT8U *)pmem - ram;
    }
    
}

void mem_init(void)
{
    struct mem *mem;
    
    mem_set(ram, 0, MEMALLOC_SIZE);
    mem = (struct mem *)ram;
    mem->next = MEMALLOC_SIZE;
    mem->prev = 0;
    mem->used = 0;
    ram_end = (struct mem *)&ram[MEMALLOC_SIZE];
    ram_end->used = 1;
    ram_end->next = MEMALLOC_SIZE;
    ram_end->prev = MEMALLOC_SIZE;
    lfree = (struct mem *)ram;
}

void mem_free(void *rmem)
{
    struct mem *mem;
    
    if (rmem == NULL) {
        return;
    }
    
    if ((INT8U *)rmem < (INT8U *)ram || (INT8U *)rmem >= (INT8U *)ram_end) {
        return;
    }
    
    mem = (struct mem *)((INT8U *)rmem - SIZEOF_STRUCT_MEM);
    mem->used = 0;

    if (mem < lfree) {
        lfree = mem;
    }
    plug_holes(mem);
}

void *mem_malloc(mem_size_t size)
{
    mem_size_t ptr, ptr2;
    struct mem *mem, *mem2;

    if (size == 0) {
        return NULL;
    }

    if ((size % MEM_ALIGNMENT) != 0) {
        size += MEM_ALIGNMENT - ((size + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT);
    }
    
    if (size > MEMALLOC_SIZE) {
        return NULL;
    }
    for (ptr = (INT8U *)lfree - ram; ptr < MEMALLOC_SIZE; ptr = ((struct mem*)&ram[ptr])->next) {
        mem = (struct mem *)&ram[ptr];
        if (!mem->used &&mem->next - (ptr + SIZEOF_STRUCT_MEM) >= size + SIZEOF_STRUCT_MEM) {
            ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
            mem2 = (struct mem *)&ram[ptr2];
            mem2->prev = ptr;
            mem2->next = mem->next;
            mem->next = ptr2;
            if (mem2->next != MEMALLOC_SIZE) {
                ((struct mem *)&ram[mem2->next])->prev = ptr2;
            }
            mem2->used = 0;
            mem->used = 1;

            if (mem == lfree) {
                while (lfree->used && lfree != ram_end) {
                    lfree = (struct mem *)&ram[lfree->next];
                }
            }
            return (INT8U *)mem + SIZEOF_STRUCT_MEM;
        }
    }

    return NULL;
}



